package com.smart.persistence.service;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;

import com.smart.service.util.Logging;



public class SmartBasicCrudDAO {

	protected static EntityManagerFactory entityManagerFactory;

	private static SmartBasicCrudDAO instance = null;
	/** A Constant LOGGER. */
	private static final Logging LOGGER = Logging.getLogger(SmartBasicCrudDAO.class
			.getName());


	
	

	public static SmartBasicCrudDAO getInstance() {
		if (instance == null) {
			instance = new SmartBasicCrudDAO();
			entityManagerFactory = Persistence
					.createEntityManagerFactory("SmartWebServiceUnit");
		}
		return instance;
	}

	private SmartBasicCrudDAO() {
		super();
	}

	/**
	 * Deveria ser utilizada a técnica de pooling para reutilização dos EntityManager ? 
	 * @return
	 */
	public EntityManager getEntityManagerDAO() {
		EntityManager entityManager = entityManagerFactory.createEntityManager();
		return entityManager;
	}

	


	protected DAOException getException(Exception ex) {
		return new DAOException(ex);
	}




	@SuppressWarnings("unchecked")
	public <ENTITY> List<ENTITY> consultaQueryList(EntityManager em, Class<ENTITY> clazz,
			String namedQuery, int numMax, int offset, Object... parametros)
			throws DAOException {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("consultaQueryList "
					+ this.getClass().getCanonicalName());
			LOGGER.debug("consultaQueryList  |--- entity \t" + clazz.getCanonicalName());
			LOGGER.debug("consultaQueryList  |--- namedList\t " + namedQuery);
			LOGGER.debug("consultaQueryList  |--- parametros\t " + parametros);
		}
		try {
			Query query = em.createNamedQuery(namedQuery);
			int i = 1;
			if (parametros != null) {
				for (Object object : parametros) {
					if (object != null) {
						LOGGER.debug("consultaQueryList          |----- parametro " + i + " \t "
								+ object);
						query.setParameter(i++, object);
					}
				}
			}
			query.setFirstResult(offset);
			query.setMaxResults(numMax);

			return query.getResultList();
		} catch (RuntimeException re) {
			LOGGER.error("consultaQueryList failed", re);
			throw getException(re);
		}

	}

	@SuppressWarnings("unchecked")
	public <ENTITY> List<ENTITY> consultaQueryList(EntityManager em, Class<ENTITY> clazz,
			String namedQuery, Object... parametros) throws DAOException {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("consultaQueryList "
					+ this.getClass().getCanonicalName());
			LOGGER.debug("consultaQueryList  |--- entity \t" + clazz.getCanonicalName());
			LOGGER.debug("consultaQueryList  |--- namedList\t " + namedQuery);
			LOGGER.debug("consultaQueryList  |--- parametros\t " + parametros);

		}
		try {
			
			Query query = em.createNamedQuery(namedQuery);
			if (parametros != null) {
				int i = 1;
				for (Object object : parametros) {
					if (object != null) {
						LOGGER.debug("consultaQueryList          |----- parametro " + i + " \t "
								+ object);
						query.setParameter(i++, object);
					}
				}
			}
			List<ENTITY> listaEntity = query.getResultList();
			LOGGER.debug("consultaQueryList  |--- return size\t " + listaEntity.size());
			return listaEntity;
		} catch (RuntimeException re) {
			LOGGER.error("consultaQueryList failed", re);
			throw getException(re);
		}

	}

	/**
	 * @param namedQuery
	 * @param parametros
	 * @return
	 * @throws java.lang.Exception
	 */
	@SuppressWarnings("unchecked")
	public <ENTITY> ENTITY consultaQuerySingle(EntityManager em, Class<ENTITY> clazz,
			String namedQuery, Object... parametros) throws DAOException {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("consultaQuerySingle "
					+ this.getClass().getCanonicalName());
			LOGGER.debug("consultaQuerySingle  |--- entity \t" + clazz.getCanonicalName());
			LOGGER.debug("consultaQuerySingle  |--- namedList\t " + namedQuery);
			LOGGER.debug("consultaQuerySingle  |--- parametros\t " + parametros);
		}
		try {
			Query query = em.createNamedQuery(namedQuery);
			if (parametros != null) {
				int i = 1;
				for (Object object : parametros) {
					if (object != null) {
						LOGGER.debug("consultaQuerySingle          |----- parametro " + i + " \t "
								+ object);
						query.setParameter(i++, object);
					}
				}
			}
			ENTITY entity = (ENTITY) query.getSingleResult();
			LOGGER.debug("consultaQuerySingle  |--- return \t " + entity);
			return entity;
		} catch (NoResultException nre) {
			LOGGER.debug("consultaQuerySingle  |--- return \t " + null);
			return null;
		} catch (RuntimeException re) {
			LOGGER.error("consultaQuerySingle failed", re);
			throw getException(re);
		}

	}
	
	


	@Override
	protected final void finalize() throws Throwable {
		super.finalize();
	}
}
